#pragma once

#include <iostream>
#include <queue>
#include <mutex>
#include <pthread.h>

#include "lockGuard.hpp"

const int gDefaultCap = 5; // 作为阻塞队列的默认容量

// 阻塞队列
template <class T>
class BlockQueue
{
private:
    // 判断队列是否为空
    bool isQueueEmpty()
    {
        return _bq.size() == 0;
    }

    // 判满
    bool isQueueFull()
    {
        return _bq.size() == _capacity; // 当size == _capacity 证明队列已满
    }

public:
    // 构造
    BlockQueue(int capacity = gDefaultCap) : _capacity(capacity)
    {
        // 初始化互斥锁 && 条件变量
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_empty, nullptr);
        pthread_cond_init(&_full, nullptr);
    }

    // 析构
    ~BlockQueue()
    {
        // 销毁 互斥锁 && 条件变量
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_full);
        pthread_cond_destroy(&_empty);
    }

    // 生产者进程
    void push(const T &in)
    {
        // 创建一个lockGuard 变量
        lockGuard lockguard(&_mtx);

        while(isQueueFull())
        {   
            // 如果此时阻塞队列为满，进程等待，直到有空位时改变_full
            pthread_cond_wait(&_full, &_mtx);
        }

        // 此时阻塞队列有空位，正常插入元素，并
        _bq.push(in);
        pthread_cond_signal(&_empty); // 发送信号，表示队列不再为空

        pthread_mutex_unlock(&_mtx);
    }

    // 消费者进程
    void pop(T *out)
    {
        lockGuard lockguard(&_mtx);
        // pthread_mutex_lock(&mtx_);
        while (isQueueEmpty())  // 如果队列为空，等待生产者制造任务
            pthread_cond_wait(&_empty, &_mtx);

        // 此时队列内有任务，
        *out = _bq.front(); // 拿_bq的头部元素，并执行pop（拿任务+销毁）
        _bq.pop();

        pthread_cond_signal(&_full);

        pthread_mutex_unlock(&_mtx);
    }

private:
    std::queue<T> _bq;   // 阻塞队列
    int _capacity; // 容量上限
    pthread_mutex_t _mtx;   // 互斥锁: 保证队列安全
    pthread_cond_t _empty; // 表示bq是否为空
    pthread_cond_t _full; // 表示bq是否为满 
};